#ifndef FILETRANSFER_HPP
#define FILETRANSFER_HPP
#include "RPC.hpp"

class FileListReceiver;
class FileRequest;
class FileReceiver;

class FileTransfer
	:public QObject
{
	Q_OBJECT
public:
	// Single file download. The complete local filename is provided directly 
	// as a parameter to begin().
	FileTransfer(ServerConnection *source, string filename);
	// File list downloaded in a stream, upload or download. The local
	// directory is provided as a parameter to begin(), and combined with
	// remote filenames to generate local filenames.
	FileTransfer(bool upload, ServerConnection *source, const RPCParams *params);
	~FileTransfer();
	
	bool isUpload();
	bool isPaused();
	
signals:
	void listReady(const vector<string> &files);
	void transferBegun();
	void fileBegun(string filename);
	void updateProgress(string filename, double progress);
	void fileFailed(string filename, string message);
	void fileFinished(string remoteFilename, string localFilename);
	void transferFinished(bool successful, string message);
	
public slots:
	void begin(string dir);
	void pause();
	void resume();
	void cancel();
	
	void setFileList(const vector<string> &files);
	void onFileFinished();
	void onFileError(string message);
	void onUpdateProgress(size_t downloaded, size_t total);
	
private:
	void finishFile();
	void startFile(string file);
	void finish();
	
	ServerConnection *host;
	bool uploading;
	bool paused;
	bool cancelling;
	
	FileListReceiver *listReceiver;
	FileRequest *request;
	FileUploader *currentUpload;
	
	int activeFile;
	vector<string> files;
	string localDir;
	string localFilename;
	bool forceFilename;
};

//////////////////////////////////////////////////////////////////////////////

class FileListReceiver
	:public QObject,
	 public StreamReceiver
{
	Q_OBJECT
public:
	FileListReceiver(RemoteHost *source, const RPCParams *params);
	void recv(int len, const char *data);
	void onClose();
	
signals:
	void setFileList(const vector<string> &files);
	
protected:
	StreamBuffer buf;
};

//////////////////////////////////////////////////////////////////////////////

class FileRequest
	:public QObject
{
	Q_OBJECT
public:
	FileRequest(FileTransfer *transfer, ServerConnection *host, string remoteFilename, string localFilename, string partialFilename);
	~FileRequest();
	void begin(RemoteHost *host, const RPCParams *params);
	void cancel();
	
public slots:
	void complete(const char *errorMessage);
	
signals:
	void finished();
	void failed(string message);
	
protected:
	int id;
	FileTransfer *transfer;
	ServerConnection *host;
	string localFilename;
	string partialFilename;
	FileReceiver *receiver;
};

//////////////////////////////////////////////////////////////////////////////

class FileReceiver
	:public QObject, public StreamReceiver
{
	Q_OBJECT
public:
	FileReceiver(FILE *file, RemoteHost *source, const RPCParams *params);
	~FileReceiver();
	
	void recv(int len, const char *data);
	void cancel();
	void onClose();
	void disconnected();
	
signals:
	void updateProgress(size_t received, size_t total);
	void finished(const char *message);
	
protected:
	FILE *file;
	FileRequest *request;
	size_t received;
	size_t size;
};

//////////////////////////////////////////////////////////////////////////////

class FileUploader
	:public QObject, public StreamSender
{
	Q_OBJECT
public:
	FileUploader(FILE *file, int id, ServerConnection *host, RPCParams *params);
	~FileUploader();
	
	void doSend();
	void cancel();
	void pause();
	void unpause();
	
signals:
	void updateProgress(size_t received, size_t total);
	void finished(const char *message);
	
protected:
	ServerConnection *host;
	int id;
	size_t size;
	size_t completed;
	FILE *file;
};

#endif
